﻿using System.Collections.Generic;
using System.Linq;
using SlamCms.Caching;
using SlamCms.Common;
using SlamCms.Web;

namespace System.Web.Mvc
{
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
	public class CacheInvalidatorFilterAttribute : ActionFilterAttribute
	{
		public CacheInvalidatorFilterAttribute()
		{
			IgnoreControllers = new string[] {};
		}

		public string[] IgnoreControllers { get; set; }

		public override void OnActionExecuting(ActionExecutingContext filterContext)
		{
			if (filterContext.IsChildAction || IsExcludedArea(filterContext) || filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.In(IgnoreControllers))
				return;

			CheckCacheInvalidation();

			base.OnActionExecuting(filterContext);
		}

		protected void CheckCacheInvalidation()
		{
			var slamCmsContext = DependencyResolver.Current.GetService<SlamCmsContext>();

			var sql = @"select Id, LastInvalidationCalled, MustInvalidate 
						from [Cache] 
						where MustInvalidate = 1
						and datediff(ms, LastInvalidationCalled, getdate()) > 100";

			var cacheSignals = slamCmsContext.Data.Database.ExecuteReader<SqlCache>(sql);

			if (!cacheSignals.Any())
				return;

			var invalidatedIds = new List<string>();
			foreach (var cacheSignal in cacheSignals)
			{
				if (cacheSignal.Id.IsNullOrWhiteSpace())
					continue;

				Invalidate(slamCmsContext, cacheSignal.Id.ToLowerInvariant());

				invalidatedIds.Add(cacheSignal.Id);
			}

			MarkInvalidated(slamCmsContext, invalidatedIds);
		}

		protected void Invalidate(SlamCmsContext slamCmsContext, string id)
		{
			switch (id)
			{
				case "tag":
					slamCmsContext.Data.Cache.InvalidateTags();
					break;
				case "user":
					slamCmsContext.Data.Cache.InvalidateUsers();
					break;
			}
		}

		protected void MarkInvalidated(SlamCmsContext slamCmsContext, IEnumerable<string> ids)
		{
			var commaSeparatedIds = string.Join(",", ids.Select(id => "'{0}'".F(id)));
			var sql = @"update [Cache] set MustInvalidate = 0 where Id in ({0})".F(commaSeparatedIds);
			slamCmsContext.Data.Database.ExecuteNonQuery(sql);
		}

		protected bool IsExcludedArea(ActionExecutingContext filterContext)
		{
			var area = (filterContext.RouteData.Values["area"] ?? string.Empty).ToString();
			if (area.In("_admin"))
				return true;
			return false;
		}
	}
}
